package com.atguigu.spzx.common.log.aspect;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.spzx.common.log.annotation.Log;
import com.atguigu.spzx.common.log.service.AsyncSysOperLogService;
import com.atguigu.spzx.common.service.utils.AuthContextUtil;
import com.atguigu.spzx.common.service.utils.IpUtil;
import com.atguigu.spzx.model.entity.system.SysOperLog;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection;
import java.util.Map;

// com.atguigu.spzx.common.log.aspect;
@Aspect
@Component
public class LogAspect {            // 环绕通知切面类定义
    @Autowired
    private AsyncSysOperLogService asyncSysOperLogService;

    @Around(value = "@annotation(log)")
    public Object doAroundAdvice(ProceedingJoinPoint joinPoint, Log log) {
        //创建sysOperLog对象
        SysOperLog sysOperLog = new SysOperLog();
        try {
            //执行目标方法之前调用的日志处理方法
            beforeHandleLog(sysOperLog, joinPoint, log);
            //执行目标的方法
            Object result = joinPoint.proceed();
            //执行目标方法之后调用的日志处理方法
            afterHandleLog(sysOperLog, joinPoint, log, result, null);
            return result;// 返回执行结果
        } catch (Throwable e) {
            //执行目标方法之后调用的日志处理方法
            afterHandleLog(sysOperLog, joinPoint, log, null, e);
            throw new RuntimeException(e);
        }

    }

    /**
     * 方法执行之前的日志处理方法
     *
     * @param sysOperLog
     * @param joinPoint
     * @param log
     */
    private void beforeHandleLog(SysOperLog sysOperLog, ProceedingJoinPoint joinPoint, Log log) {
        //获取request对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取并设置请求方式
        String requestMethod = request.getMethod();
        sysOperLog.setRequestMethod(requestMethod);
        //获取并设置请求地址
        sysOperLog.setOperUrl(request.getRequestURI());
        //获取并设置ip地址
        sysOperLog.setOperIp(IpUtil.getIpAddress(request));
        //获取当前用户的名字
        String username = AuthContextUtil.get().getUsername();
        //设置操作人员的名字
        sysOperLog.setOperName(username);
        //获取并设置操作模块
        sysOperLog.setTitle(log.title());
        //获取并设置操作类型
        sysOperLog.setOperatorType(log.operatorType().name());
        //获取并设置业务类型
        sysOperLog.setBusinessType(log.businessType().name());
        //获取被代理对象的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取目标方法名
        String methodName = joinPoint.getSignature().getName();
        //拼接并设置调用完整方法名
        sysOperLog.setMethod(className + "." + methodName + "()");
        //判断是否需要保存数据
        if (log.isSaveRequestData()) {
            //判断是否是Post请求或Put请求
            if (HttpMethod.POST.name().equals(requestMethod) || HttpMethod.PUT.name().equals(requestMethod)) {
                Object[] args = joinPoint.getArgs();
                String OperParam = argsArrayToString(args);
                sysOperLog.setOperParam(OperParam);
            }
        }
    }

    /**
     * 方法执行之后的日志处理方法
     *
     * @param sysOperLog
     * @param joinPoint
     * @param result
     * @param e
     */
    public void afterHandleLog(SysOperLog sysOperLog, ProceedingJoinPoint joinPoint, Log log, Object result, Throwable e) {
        //判断是否保存响应数据
        if (log.isSaveResponseData()) {
            //将结果转换为Json字符串
            String jsonResult = JSONObject.toJSONString(result);
            //设置返回参数
            sysOperLog.setJsonResult(jsonResult);
            //设置操作状态
            sysOperLog.setStatus(0);
        }
        //判断是否有异常
        if (e != null) {
            //设置操作状态
            sysOperLog.setStatus(1);
            //设置错误消息
            sysOperLog.setErrorMsg(e.getMessage());
        }
        //调用保存日志方法
        asyncSysOperLogService.saveSysLog(sysOperLog);
    }


    // 参数拼装
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (!StringUtils.isEmpty(o) && !isFilterObject(o)) {
                    try {
                        Object jsonObj = JSON.toJSON(o);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {       // 判断是否为数组类型
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);  // 如果是数组，判断其元素类型是否为MultipartFile或其子类
        } else if (Collection.class.isAssignableFrom(clazz)) { // 判断是否为Collection集合类型
            Collection collection = (Collection) o;
            for (Object value : collection) {  // 只要有一个元素的类型为MultipartFile或其子类，则认为该集合对象为过滤对象
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {  // 判断是否为Map集合类型
            Map map = (Map) o;
            for (Object value : map.entrySet()) {  // 只要有一个Value的类型是MultipartFile或其子类，则认为该映射对象为过滤对象
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }

        // 如果以上条件都不满足，最后判断对象本身是否为MultipartFile、HttpServletRequest、HttpServletResponse类的实例
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}
